use core::fmt;
use std::borrow::Cow;
use std::error;

/// Sum type of all errors possibly returned from `Regexp` APIs.
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum Error {
    /// Error that indicates an argument parsing or value logic error occurred.
    ///
    /// See [`ArgumentError`].
    Argument(ArgumentError),
    /// Error that indicates a `Regexp` was malformed at runtime.
    ///
    /// See [`RegexpError`].
    Regexp(RegexpError),
    /// Error that indicates a given `Regexp` pattern could not be parsed when
    /// given as a `/.../` literal in Ruby source code.
    ///
    /// See [`SyntaxError`].
    Syntax(SyntaxError),
}

impl From<ArgumentError> for Error {
    #[inline]
    fn from(err: ArgumentError) -> Self {
        Self::Argument(err)
    }
}

impl From<RegexpError> for Error {
    #[inline]
    fn from(err: RegexpError) -> Self {
        Self::Regexp(err)
    }
}

impl From<SyntaxError> for Error {
    #[inline]
    fn from(err: SyntaxError) -> Self {
        Self::Syntax(err)
    }
}

impl fmt::Display for Error {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("Regexp error")
    }
}

impl error::Error for Error {
    #[inline]
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match self {
            Self::Argument(err) => Some(err),
            Self::Regexp(err) => Some(err),
            Self::Syntax(err) => Some(err),
        }
    }
}

/// Error that indicates an argument parsing or value logic error occurred.
///
/// Argument errors have an associated message.
///
/// This error corresponds to the [Ruby `ArgumentError` Exception class].
///
/// # Examples
///
/// ```
/// # use spinoso_regexp::ArgumentError;
/// let err = ArgumentError::new();
/// assert_eq!(err.message(), "ArgumentError");
///
/// let err = ArgumentError::with_message("invalid byte sequence in UTF-8");
/// assert_eq!(err.message(), "invalid byte sequence in UTF-8");
/// ```
///
/// [Ruby `ArgumentError` Exception class]: https://ruby-doc.org/core-3.1.2/ArgumentError.html
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct ArgumentError(Cow<'static, str>);

impl From<&'static str> for ArgumentError {
    #[inline]
    fn from(message: &'static str) -> Self {
        Self::with_message(message)
    }
}

impl From<String> for ArgumentError {
    fn from(message: String) -> Self {
        Self(Cow::Owned(message))
    }
}

impl Default for ArgumentError {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl fmt::Display for ArgumentError {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.message())
    }
}

impl error::Error for ArgumentError {}

impl ArgumentError {
    /// Construct a new, default argument error.
    ///
    /// # Examples
    ///
    /// ```
    /// # use spinoso_regexp::ArgumentError;
    /// const ERR: ArgumentError = ArgumentError::new();
    /// assert_eq!(ERR.message(), "ArgumentError");
    /// ```
    #[inline]
    #[must_use]
    pub const fn new() -> Self {
        Self(Cow::Borrowed("ArgumentError"))
    }

    /// Construct a new argument error with a message.
    ///
    /// # Examples
    ///
    /// ```
    /// # use spinoso_regexp::ArgumentError;
    /// const ERR: ArgumentError = ArgumentError::with_message("invalid byte sequence in UTF-8");
    /// assert_eq!(ERR.message(), "invalid byte sequence in UTF-8");
    /// ```
    #[inline]
    #[must_use]
    pub const fn with_message(message: &'static str) -> Self {
        Self(Cow::Borrowed(message))
    }

    #[must_use]
    pub(crate) const fn unsupported_pattern_encoding() -> Self {
        Self::with_message("Unsupported pattern encoding")
    }

    #[must_use]
    pub(crate) const fn unsupported_haystack_encoding() -> Self {
        Self::with_message("Unsupported haystack encoding")
    }

    /// Retrieve the exception message associated with this argument error.
    ///
    /// # Examples
    ///
    /// ```
    /// # use spinoso_regexp::ArgumentError;
    /// let err = ArgumentError::new();
    /// assert_eq!(err.message(), "ArgumentError");
    ///
    /// let err = ArgumentError::with_message("invalid byte sequence in UTF-8");
    /// assert_eq!(err.message(), "invalid byte sequence in UTF-8");
    /// ```
    #[inline]
    #[must_use]
    pub fn message(&self) -> &str {
        self.0.as_ref()
    }
}

/// Error that indicates a `Regexp` was malformed at runtime.
///
/// This error is typically generated by [`Regexp::compile`].
///
/// This error corresponds to the [Ruby `RegexpError` Exception class].
///
/// # Examples
///
/// ```
/// # use spinoso_regexp::RegexpError;
/// let err = RegexpError::new();
/// assert_eq!(err.message(), "RegexpError");
///
/// let err = RegexpError::with_message(r"invalid multibyte character: /\xFF\xFE/");
/// assert_eq!(err.message(), r"invalid multibyte character: /\xFF\xFE/");
/// ```
///
/// [`Regexp::compile`]: https://ruby-doc.org/core-3.1.2/Regexp.html#method-c-compile
/// [Ruby `RegexpError` Exception class]: https://ruby-doc.org/core-3.1.2/RegexpError.html
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct RegexpError(Cow<'static, str>);

impl From<&'static str> for RegexpError {
    #[inline]
    fn from(message: &'static str) -> Self {
        Self::with_message(message)
    }
}

impl From<String> for RegexpError {
    fn from(message: String) -> Self {
        Self(Cow::Owned(message))
    }
}

impl fmt::Display for RegexpError {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.message())
    }
}

impl error::Error for RegexpError {}

impl Default for RegexpError {
    fn default() -> Self {
        Self::new()
    }
}

impl RegexpError {
    /// Construct a new, default regexp error.
    ///
    /// # Examples
    ///
    /// ```
    /// # use spinoso_regexp::RegexpError;
    /// const ERR: RegexpError = RegexpError::new();
    /// assert_eq!(ERR.message(), "RegexpError");
    /// ```
    #[inline]
    #[must_use]
    pub const fn new() -> Self {
        Self(Cow::Borrowed("RegexpError"))
    }

    /// Construct a new regexp error with a message.
    ///
    /// # Examples
    ///
    /// ```
    /// # use spinoso_regexp::RegexpError;
    /// const ERR: RegexpError = RegexpError::with_message(r"invalid multibyte character: /\xFF\xFE/");
    /// assert_eq!(ERR.message(), r"invalid multibyte character: /\xFF\xFE/");
    /// ```
    #[inline]
    #[must_use]
    pub const fn with_message(message: &'static str) -> Self {
        Self(Cow::Borrowed(message))
    }

    /// Retrieve the exception message associated with this regexp error.
    ///
    /// # Examples
    ///
    /// ```
    /// # use spinoso_regexp::RegexpError;
    /// let err = RegexpError::new();
    /// assert_eq!(err.message(), "RegexpError");
    ///
    /// let err = RegexpError::with_message(r"invalid multibyte character: /\xFF\xFE/");
    /// assert_eq!(err.message(), r"invalid multibyte character: /\xFF\xFE/");
    /// ```
    #[inline]
    #[must_use]
    pub fn message(&self) -> &str {
        &self.0
    }
}

/// Error that indicates a given `Regexp` pattern could not be parsed when given
/// as a `/.../` literal in Ruby source code.
///
/// This error is typically generated at parse-time.
///
/// This error corresponds to the [Ruby `SyntaxError` Exception class].
///
/// # Examples
///
/// ```
/// # use spinoso_regexp::SyntaxError;
/// let err = SyntaxError::new();
/// assert_eq!(err.message(), "SyntaxError");
///
/// let err = SyntaxError::with_message("premature end of char-class");
/// assert_eq!(err.message(), "premature end of char-class");
/// ```
///
/// [Ruby `SyntaxError` Exception class]: https://ruby-doc.org/core-3.1.2/SyntaxError.html
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct SyntaxError(Cow<'static, str>);

impl From<&'static str> for SyntaxError {
    #[inline]
    fn from(message: &'static str) -> Self {
        Self::with_message(message)
    }
}

impl From<String> for SyntaxError {
    fn from(message: String) -> Self {
        Self(Cow::Owned(message))
    }
}

impl fmt::Display for SyntaxError {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.message())
    }
}

impl error::Error for SyntaxError {}

impl Default for SyntaxError {
    fn default() -> Self {
        Self::new()
    }
}

impl SyntaxError {
    /// Construct a new, default syntax error.
    ///
    /// # Examples
    ///
    /// ```
    /// # use spinoso_regexp::SyntaxError;
    /// const ERR: SyntaxError = SyntaxError::new();
    /// assert_eq!(ERR.message(), "SyntaxError");
    /// ```
    #[inline]
    #[must_use]
    pub const fn new() -> Self {
        Self(Cow::Borrowed("SyntaxError"))
    }

    /// Construct a new syntax error with a message.
    ///
    /// # Examples
    ///
    /// ```
    /// # use spinoso_regexp::SyntaxError;
    /// const ERR: SyntaxError = SyntaxError::with_message("premature end of char-class");
    /// assert_eq!(ERR.message(), "premature end of char-class");
    /// ```
    #[inline]
    #[must_use]
    pub const fn with_message(message: &'static str) -> Self {
        Self(Cow::Borrowed(message))
    }

    /// Retrieve the exception message associated with this syntax error.
    ///
    /// # Examples
    ///
    /// ```
    /// # use spinoso_regexp::SyntaxError;
    /// let err = SyntaxError::new();
    /// assert_eq!(err.message(), "SyntaxError");
    ///
    /// let err = SyntaxError::with_message("premature end of char-class");
    /// assert_eq!(err.message(), "premature end of char-class");
    /// ```
    #[inline]
    #[must_use]
    pub fn message(&self) -> &str {
        &self.0
    }
}
